home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 25 / AACD 25.iso / AACD / Magazine / Online / QMail / source / qmail-smtpd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-10-02  |  17.9 KB  |  726 lines

  1. #define BADRCPT 1
  2. #define SYSLOGGING 1
  3. #define DENYSPAM 1
  4. #define MAILFROMCHECK 1
  5. #define MAILFROMDNS 1
  6. /*zzz Version 0.9r1.0*/
  7. #include "sig.h"
  8. #include "readwrite.h"
  9. #include "getln.h"
  10. #include "stralloc.h"
  11. #include "substdio.h"
  12. #ifdef SYSLOGGING
  13. #ifdef __amigaos__ /* Work around syslog.h bug.  */
  14. #include <machine/ansi.h>
  15. #endif
  16. #include <syslog.h>
  17. #endif
  18. #include "alloc.h"
  19. #include "auto_qmail.h"
  20. #include "control.h"
  21. #include "received.h"
  22. #include "constmap.h"
  23. #include "error.h"
  24. #include "ipme.h"
  25. #include "ip.h"
  26. #include "qmail.h"
  27. #include "str.h"
  28. #include "fmt.h"
  29. #include "byte.h"
  30. #include "case.h"
  31. #include "env.h"
  32. #include "now.h"
  33. #include "exit.h"
  34. #ifdef MAILFROMDNS
  35. #include "dns.h"
  36. #endif
  37.  
  38. #define MAXHOPS 100
  39. int timeout = 1200;
  40.  
  41. char ssoutbuf[512];
  42. substdio ssout = SUBSTDIO_FDBUF(write,1,ssoutbuf,sizeof(ssoutbuf));
  43.  
  44. void die() { substdio_flush(&ssout); closelog (); _exit(1); }
  45. void flush() { if (substdio_flush(&ssout) == -1) { closelog (); _exit(1); } }
  46. void out(s) char *s; { if (substdio_puts(&ssout,s) == -1) die(); }
  47.  
  48. int timeoutread(fd,buf,n) int fd; char *buf; int n;
  49. {
  50.  int r; int saveerrno;
  51.  flush();
  52.  alarm(timeout);
  53.  r = read(fd,buf,n); saveerrno = errno;
  54.  alarm(0);
  55.  errno = saveerrno; return r;
  56. }
  57.  
  58. char ssinbuf[1024];
  59. substdio ssin = SUBSTDIO_FDBUF(timeoutread,0,ssinbuf,sizeof(ssinbuf));
  60.  
  61.  
  62. struct qmail qqt;
  63. stralloc greeting = {0};
  64. int liphostok = 0;
  65. stralloc liphost = {0};
  66. int rhok = 0;
  67. stralloc rcpthosts = {0};
  68. struct constmap maprcpthosts;
  69. int bmfok = 0;
  70. stralloc bmf = {0};
  71. struct constmap mapbmf;
  72. int flagbarf; /* defined if seenmail */
  73. #ifdef BADRCPT
  74. int brtok = 0;
  75. stralloc brt = {0};
  76. struct constmap mapbadrcptto;
  77. #endif
  78.  
  79. stralloc helohost = {0};
  80. stralloc mailfrom = {0};
  81. stralloc rcptto = {0};
  82. stralloc relayclients = {0};
  83. stralloc relaydomains = {0};
  84. struct constmap maprelayclients, maprelaydomains;
  85. int seenmail = 0;
  86.  
  87. stralloc addr = {0}; /* will be 0-terminated, if addrparse returns 1 */
  88.  
  89. char *remoteip;
  90. char *remotehost;
  91. char *remoteinfo;
  92. char *local;
  93. char *relayclient;
  94. #ifdef DENYSPAM
  95. char *denymail;
  96. #endif
  97.  
  98. void outofmem()
  99. {
  100. #ifdef SYSLOGGING
  101.   syslog (LOG_ERR, "Out of memory while connected to %s!\n", remoteip);
  102. #endif
  103.   out("421 out of memory (#4.3.0)\r\n"); die();
  104. }
  105.  
  106. void sigalrm()
  107. {
  108. #ifdef SYSLOGGING
  109.   syslog (LOG_INFO, "Connection to %s timed out.\n", remoteip);
  110. #endif
  111.   out("451 timeout (#4.4.2)\r\n"); die();
  112. }
  113.  
  114. void dohelo(arg) char *arg;
  115. {
  116.  if (!stralloc_copys(&helohost,arg)) outofmem(); 
  117.  if (!stralloc_0(&helohost)) outofmem(); 
  118. }
  119.  
  120. void getenvs()
  121. {
  122.  unsigned short int error = 0;
  123.  unsigned int i, len;
  124.  
  125.  remoteip = env_get("TCPREMOTEIP");
  126.  if (!remoteip) remoteip = "unknown";
  127.  local = env_get("TCPLOCALHOST");
  128.  if (!local) local = env_get("TCPLOCALIP");
  129.  if (!local) local = "unknown";
  130.  remotehost = env_get("TCPREMOTEHOST");
  131.  if (!remotehost) remotehost = "unknown";
  132.  remoteinfo = env_get("TCPREMOTEINFO");
  133.  relayclient = env_get("RELAYCLIENT");
  134.  if (! relayclient)
  135.  {
  136.    /* Attempt to look up the IP number in control/relayclients. */
  137.    switch (control_readfile (&relayclients, "control/relayclients", 0))
  138.    {
  139.      case -1:
  140.      if (errno == error_nomem)
  141.        outofmem ();
  142.      /* Fall through. */
  143.  
  144.      case 0:
  145.      error = 1;
  146.      break;
  147.  
  148.      case 1:
  149.      if (!constmap_init (&maprelayclients, relayclients.s, relayclients.len, 1))
  150.        outofmem ();
  151.      break;
  152.    }
  153.  
  154.   if (! error)
  155.   {
  156.     for (i = len = str_len (remoteip); i > 0; i --)
  157.       if ((i == len) || (remoteip[i - 1] == '.'))
  158.         if ((relayclient = constmap (&maprelayclients, remoteip, i)))
  159.           break;
  160.   }
  161.  }
  162.  
  163.  error = 0;
  164.  if (! relayclient)
  165.  {
  166.    /* Attempt to look up the host name in control/relaydomains. */
  167.    switch (control_readfile (&relaydomains, "control/relaydomains", 0))
  168.    {
  169.      case -1:
  170.      if (errno == error_nomem)
  171.        outofmem ();
  172.      /* Fall through. */
  173.  
  174.      case 0:
  175.      error = 1;
  176.      break;
  177.  
  178.      case 1:
  179.      if (!constmap_init (&maprelaydomains, relaydomains.s, relaydomains.len, 1))
  180.        outofmem ();
  181.      break;
  182.    }
  183.  
  184.   if (! error)
  185.   {
  186.     for (i = 0, len = str_len (remotehost); i <= len; i ++)
  187.       if ((i == 0) || (i == len) || (remotehost[i] == '.'))
  188.         if ((relayclient = constmap (&maprelaydomains, remotehost + i, len - i)))
  189.           break;
  190.   }
  191.  }
  192.  
  193.  dohelo(remotehost);
  194. #ifdef DENYSPAM
  195.  denymail = env_get("DENYMAIL");
  196. #endif
  197. }
  198.  
  199. void blast(ssfrom,hops)
  200. substdio *ssfrom;
  201. int *hops;
  202. {
  203.  char ch;
  204.  int state;
  205.  int flaginheader;
  206.  int pos; /* number of bytes since most recent \n, if fih */
  207.  int flagmaybex; /* 1 if this line might match RECEIVED, if fih */
  208.  int flagmaybey; /* 1 if this line might match \r\n, if fih */
  209.  int flagmaybez; /* 1 if this line might match DELIVERED, if fih */
  210.  
  211.  state = 1;
  212.  *hops = 0;
  213.  flaginheader = 1;
  214.  pos = 0; flagmaybex = flagmaybey = flagmaybez = 1;
  215.  for (;;)
  216.   {
  217.    if (substdio_get(ssfrom,&ch,1) <= 0) die();
  218.    if (flaginheader)
  219.     {
  220.      if (pos < 9)
  221.       {
  222.        if (ch != "delivered"[pos]) if (ch != "DELIVERED"[pos]) flagmaybez = 0;
  223.        if (flagmaybez) if (pos == 8) ++*hops;
  224.        if (pos < 8)
  225.          if (ch != "received"[pos]) if (ch != "RECEIVED"[pos]) flagmaybex = 0;
  226.        if (flagmaybex) if (pos == 7) ++*hops;
  227.        if (pos < 2) if (ch != "\r\n"[pos]) flagmaybey = 0;
  228.        if (flagmaybey) if (pos == 1) flaginheader = 0;
  229.       }
  230.      ++pos;
  231.      if (ch == '\n') { pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; }
  232.     }
  233.    switch(state)
  234.     {
  235.      case 0:
  236.        if (ch == '\r') { state = 4; continue; }
  237.        break;
  238.      case 1: /* \r\n */
  239.        if (ch == '.') { state = 2; continue; }
  240.        if (ch == '\r') { state = 4; continue; }
  241.        state = 0;
  242.        break;
  243.      case 2: /* \r\n + . */
  244.        if (ch == '\r') { state = 3; continue; }
  245.        state = 0;
  246.        break;
  247.      case 3: /* \r\n + .\r */
  248.        if (ch == '\n') return;
  249.        qmail_put(&qqt,".\r",2);
  250.        if (ch == '\r') { state = 4; continue; }
  251.        state = 0;
  252.        break;
  253.      case 4: /* + \r */
  254.        if (ch == '\n') { state = 1; break; }
  255.        if (ch != '\r') { qmail_put(&qqt,"\r",1); state = 0; }
  256.     }
  257.    qmail_put(&qqt,&ch,1);
  258.   }
  259. }
  260.  
  261. int addrparse(arg)
  262. char *arg;
  263. {
  264.  int i;
  265.  char ch;
  266.  struct ip_address ip;
  267.  int flagesc;
  268.  int flagquoted;
  269.  
  270.  arg += str_chr(arg,'<');
  271.  if (*arg != '<') return 0;
  272.  ++arg;
  273.  
  274.  /* strip source route */
  275.  if (*arg == '@') while (*arg) if (*arg++ == ':') break;
  276.  
  277.  if (!*arg) return 0;
  278.  if (!stralloc_copys(&addr,"")) outofmem();
  279.  flagesc = 0;
  280.  flagquoted = 0;
  281.  for (i = 0;ch = arg[i];++i) /* copy arg to addr, stripping quotes */
  282.   {
  283.    if (flagesc)
  284.     { if (!stralloc_append(&addr,&ch)) outofmem(); flagesc = 0; }
  285.    else
  286.     {
  287.      if (!flagquoted && (ch == '>')) break;
  288.      switch(ch)
  289.       {
  290.        case '\\': flagesc = 1; break;
  291.        case '"': flagquoted = !flagquoted; break;
  292.        default: if (!stralloc_append(&addr,&ch)) outofmem();
  293.       }
  294.     }
  295.   }
  296.  if (!ch) return 0;
  297.  if (!stralloc_append(&addr,"")) outofmem();
  298.  ++i;
  299.  while (arg[i])
  300.   {
  301.    if (!case_diffs(arg + i," BODY=8BITMIME")) i += 14;
  302.    else if (!case_diffs(arg + i," BODY=7BIT")) i += 10;
  303.    else return 0;
  304.   }
  305.  
  306.  if (liphostok)
  307.   {
  308.    i = byte_rchr(addr.s,addr.len,'@');
  309.    if (i < addr.len) /* if not, partner should go read rfc 821 */
  310.      if (addr.s[i + 1] == '[')
  311.        if (!addr.s[i + 1 + ip_scanbracket(addr.s + i + 1,&ip)])
  312.          if (ipme_is(&ip))
  313.           {
  314.            addr.len = i + 1;
  315.            if (!stralloc_cat(&addr,&liphost)) outofmem();
  316.            if (!stralloc_0(&addr)) outofmem();
  317.           }
  318.   }
  319.  
  320.  return 1;
  321. }
  322.  
  323. #ifdef SYSLOGGING
  324.  
  325. static void log_deny(m,f,t) char *m,*f,*t;
  326. {
  327.   syslog (LOG_INFO, "%s check failed (%s) -> (%s) [%s] (HELO %s)",
  328.           m, f, t, remoteip, helohost.s);
  329.  
  330. #ifdef BADRCPT
  331. static void log_brt(s,r) char *s,*r;
  332. {
  333.   log_deny("Bad RCPT TO:", s, r);
  334. #endif
  335.  
  336. static void log_bmf(s,r) char *s,*r;
  337. {
  338.   log_deny("Bad MAIL FROM:", s, r);
  339.  
  340. static void log_ngw (char *s, char *r)
  341. {
  342.   log_deny ("Relayclient", s, r);
  343. }
  344.  
  345. static void log_helo()
  346. {
  347.   syslog (LOG_INFO, "Received: from %s (HELO %s)\n", remotehost, helohost.s);
  348.  
  349. #else /* not SYSLOGGING */
  350. #define log_brt(s,r)
  351. #define log_bmf(s,r)
  352. #define log_deny(m,f,t)
  353. #define log_helo()
  354. #endif /* not SYSLOGGING */
  355.  
  356. #ifdef MAILFROMDNS
  357. int badmxcheck(dom) char *dom;
  358. {
  359.   ipalloc checkip = {0};
  360.   int ret=0;
  361.   stralloc checkhost = {0};
  362.  
  363.   if (!*dom) return (DNS_HARD);
  364.   if (!stralloc_copys(&checkhost,dom)) return (DNS_SOFT);
  365.   
  366.   switch (dns_mxip(&checkip,&checkhost,1))
  367.   {
  368.     case DNS_MEM:
  369.     case DNS_SOFT:
  370.          ret=DNS_SOFT;
  371.          break;
  372.          
  373.     case DNS_HARD: 
  374.          ret=DNS_HARD; 
  375.          break;
  376.     case 1:
  377.          if (checkip.len <= 0) ret=DNS_HARD; 
  378.          break;
  379.   }
  380.  
  381.   return (ret);
  382. }
  383. #endif
  384.  
  385. int addrallowed()
  386. {
  387.  int j;
  388.  if (!rhok) return 1;
  389.  j = byte_rchr(addr.s,addr.len,'@');
  390. #ifdef BADRCPT
  391.  if (brtok)
  392.     if (constmap(&mapbadrcptto, addr.s, addr.len - 1) ||
  393.         constmap(&mapbadrcptto, addr.s + j, addr.len - j - 1))
  394.        {log_brt(mailfrom.s,addr.s); return 0;}
  395. #endif
  396.  if (j >= addr.len) return 1; /* can be taken care of by envnoathost */
  397.  if (constmap(&maprcpthosts,addr.s + j + 1,addr.len - j - 2)) return 1;
  398.  for (;j < addr.len;++j)
  399.    if (addr.s[j] == '.')
  400.      if (constmap(&maprcpthosts,addr.s + j,addr.len - j - 1)) return 1;
  401.  log_ngw (mailfrom.s, addr.s);
  402.  return 0;
  403. }
  404.  
  405. void bmfcheck()
  406. {
  407.  int j;
  408.  flagbarf = 0;
  409.  if (!bmfok) return;
  410.  if (constmap(&mapbmf,addr.s,addr.len - 1)) { flagbarf = 1; return; }
  411.  j = byte_rchr(addr.s,addr.len,'@');
  412.  if (j < addr.len)
  413. #ifdef DENYSPAM 
  414.  {
  415. #endif
  416.    if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) { flagbarf = 1; return; }
  417. #ifdef DENYSPAM 
  418.    /* allow for bulkemailer@ checking */
  419.    if (constmap(&mapbmf,addr.s, j + 1)) { flagbarf = 1; return; }
  420.  }  
  421. #endif
  422. }
  423.  
  424. void smtp_greet(code) char *code; {
  425.  if (substdio_puts(&ssout,code) == -1) die();
  426.  if (substdio_put(&ssout,greeting.s,greeting.len) == -1) die(); }
  427. void smtp_quit() { smtp_greet("221 "); out("\r\n"); die(); }
  428. void smtp_help() { out("214-qmail home page: http://pobox.com/~djb/qmail.html\r\n214 send comments to qmail@pobox.com\r\n"); }
  429. void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); }
  430. /* zzz 
  431.  void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); }
  432. */
  433. void err_bmf() { out("553 syntax error, please forward to your postmaster (#5.7.1)\r\n"); }
  434. void err_dns() { out("451 DNS temporary failure (#4.3.0)\r\n"); }
  435. void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); }
  436. void err_unimpl() { out("502 unimplemented (#5.5.1)\r\n"); }
  437. void err_seenmail() { out("503 one MAIL per message (#5.5.1)\r\n"); }
  438. void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); }
  439. void err_wantrcpt() { out("503 RCPT first (#5.5.1)\r\n"); }
  440. void err_noop() { out("250 ok\r\n"); }
  441. void err_vrfy() { out("252 send some mail, i'll try my best\r\n"); }
  442. void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); }
  443. void smtp_helo(arg) char *arg; {
  444.  smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n");
  445.  seenmail = 0;
  446.  dohelo(arg ? arg : ""); }
  447. void smtp_rset() {
  448.  seenmail = 0;
  449.  out("250 flushed\r\n"); }
  450. void smtp_mail(arg) char *arg; {
  451. int i,j; char *why;
  452.  if (seenmail) { err_seenmail(); return; }
  453.  if (!arg) { err_syntax(); return; }
  454.  if (!addrparse(arg)) { err_syntax(); return; }
  455.  bmfcheck();
  456.  if (flagbarf) { log_bmf(addr.s,""); err_bmf(); return; }
  457. #ifdef DENYSPAM
  458. /************
  459.    DENYMAIL is set for this session from this client, 
  460.              so heavy checking of mailfrom
  461.    SPAM     -> refuse all mail
  462.    NOBOUNCE -> refuse null mailfrom
  463.    DNSCHECK -> validate Mailfrom domain
  464. ************/
  465.  
  466.  if (denymail)
  467.  {
  468.     why = denymail;
  469.     
  470.     if (!str_diff("SPAM", denymail)) 
  471.        flagbarf=1;
  472.     else
  473.       if (!addr.s[0] || !str_diff("#@[]", addr.s)) /*mjr*/
  474.      /* if (!addr.s[0]) */
  475.       {  
  476.          if (!str_diff("NOBOUNCE", denymail)) 
  477.             flagbarf=1;
  478.       }
  479. #ifdef MAILFROMCHECK
  480.       else
  481.       {
  482.         /*why = "Invalid.Mailfrom";*/
  483.         why = "MAIL FROM: syntax";
  484.         if ((i=byte_chr(addr.s,addr.len,'@')) >= addr.len)
  485.            flagbarf=1;       /* no '@' in from */
  486.         else
  487.         {
  488.           /* money!@domain.TLD */
  489.           if (addr.s[i-1] == '!') 
  490.              flagbarf=1;
  491.              
  492.           /* check syntax, visual */
  493.           if ((j = byte_rchr(addr.s+i, addr.len-i, '.')) >= addr.len-i)
  494.              flagbarf=1;  /* curious no '.' in domain.TLD */
  495.          
  496.           j = addr.len-(i+1+j+1);
  497.           if (j < 2 || j > 3)
  498.              flagbarf=1;  /* root domain, not a country (2), nor TLD (3)*/
  499.  
  500. #ifdef MAILFROMDNS
  501.          if (!flagbarf)
  502.           if (!str_diff("DNSCHECK", denymail)) 
  503.           {
  504.            /* check syntax, via DNS */
  505.              why = "MAIL FROM: DNS";
  506.              switch (badmxcheck(&addr.s[i+1]))
  507.              {
  508.                case 0:         break; /*valid*/
  509.                case DNS_SOFT:    flagbarf=2; /*fail tmp*/
  510.                                 why = "(temporary) MAIL FROM: DNS";
  511.                                 break;
  512.                case DNS_HARD:     flagbarf=1; 
  513.                            break;
  514.              }
  515.           }
  516. #endif
  517.         }
  518.       }
  519. #endif
  520.  
  521.     if (flagbarf)    
  522.     {
  523.       log_deny(why, addr.s, ""); 
  524.       if (2==flagbarf)
  525.          err_dns(); 
  526.       else
  527.          err_bmf();
  528.       return;
  529.     }
  530.  }/* denymail */
  531. #endif   
  532.  seenmail = 1; out("250 ok\r\n");
  533.  if (!stralloc_copys(&rcptto,"")) outofmem();
  534.  if (!stralloc_copys(&mailfrom,addr.s)) outofmem();
  535.  if (!stralloc_0(&mailfrom)) outofmem(); }
  536. void smtp_rcpt(arg) char *arg; {
  537.  if (!seenmail) { err_wantmail(); return; }
  538.  if (!arg) { err_syntax(); return; }
  539.  if (!addrparse(arg)) { err_syntax(); return; }
  540.  if (relayclient)
  541.   {
  542.    --addr.len;
  543.    if (!stralloc_cats(&addr,relayclient)) outofmem();
  544.    if (!stralloc_0(&addr)) outofmem();
  545.   }
  546.  else
  547.    if (!addrallowed()) { err_nogateway(); return; }
  548.  out("250 ok\r\n");
  549.  if (!stralloc_cats(&rcptto,"T")) outofmem();
  550.  if (!stralloc_cats(&rcptto,addr.s)) outofmem();
  551.  if (!stralloc_0(&rcptto)) outofmem(); 
  552. }
  553.  
  554. char accept_buf[FMT_ULONG];
  555. void acceptmessage(qp) unsigned long qp;
  556. {
  557.  datetime_sec when;
  558.  when = now();
  559.  out("250 ok ");
  560.  accept_buf[fmt_ulong(accept_buf,(unsigned long) when)] = 0;
  561.  out(accept_buf);
  562.  out(" qp ");
  563.  accept_buf[fmt_ulong(accept_buf,qp)] = 0;
  564.  out(accept_buf);
  565.  out("\r\n");
  566. }
  567.  
  568. void smtp_data() {
  569.  int hops; int r; unsigned long qp;
  570.  if (!seenmail) { err_wantmail(); return; }
  571.  if (!rcptto.len) { err_wantrcpt(); return; }
  572.  seenmail = 0;
  573.  if (qmail_open(&qqt) == -1) { err_qqt(); return; }
  574.  qp = qmail_qp(&qqt);
  575.  out("354 go ahead\r\n");
  576.  
  577.  received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,(r=case_diffs(remotehost,helohost.s)) ? helohost.s : 0);
  578.  if (r) log_helo();
  579.  blast(&ssin,&hops);
  580.  hops = (hops >= MAXHOPS);
  581.  if (hops) qmail_fail(&qqt);
  582.  qmail_from(&qqt,mailfrom.s);
  583.  qmail_put(&qqt,rcptto.s,rcptto.len);
  584.  
  585.  r = qmail_close(&qqt);
  586.  if (!r) { acceptmessage(qp); return; }
  587.  if (hops) { out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; }
  588.  switch(r)
  589.   {
  590.    case QMAIL_TOOLONG: out("554 address too long (#5.1.3)\r\n"); return;
  591.    case QMAIL_SYS: out("451 qq system error (#4.3.0)\r\n"); return;
  592.    case QMAIL_READ: out("451 qq read error (#4.3.0)\r\n"); return;
  593.    case QMAIL_WRITE: out("451 qq write error or disk full (#4.3.0)\r\n"); return;
  594.    case QMAIL_NOMEM: out("451 qq out of memory (#4.3.0)\r\n"); return;
  595.    case QMAIL_EXECSOFT: out("451 could not exec qq (#4.3.0)\r\n"); return;
  596.    case QMAIL_TIMEOUT: out("451 qq timeout (#4.3.0)\r\n"); return;
  597.    case QMAIL_WAITPID: out("451 qq waitpid surprise (#4.3.0)\r\n"); return;
  598.    case QMAIL_CRASHED: out("451 qq crashed (#4.3.0)\r\n"); return;
  599.    case QMAIL_USAGE: out("451 qq usage surprise (#4.3.0)\r\n"); return;
  600.    default: out("451 qq internal bug (#4.3.0)\r\n"); return;
  601.   }
  602. }
  603.  
  604. static struct { void (*fun)(); char *text; int flagflush; } smtpcmd[] = {
  605.   { smtp_rcpt, "rcpt", 0 }
  606. , { smtp_mail, "mail", 0 }
  607. , { smtp_data, "data", 1 }
  608. , { smtp_quit, "quit", 1 }
  609. , { smtp_helo, "helo", 1 }
  610. , { smtp_helo, "ehlo", 1 }
  611. , { smtp_rset, "rset", 0 }
  612. , { smtp_help, "help", 1 }
  613. , { err_noop, "noop", 1 }
  614. , { err_vrfy, "vrfy", 1 }
  615. , { 0, 0, 0 }
  616. };
  617.  
  618. void doit(cmd)
  619. char *cmd;
  620. {
  621.  int i;
  622.  int j;
  623.  char ch;
  624.  
  625.  for (i = 0;smtpcmd[i].fun;++i)
  626.   {
  627.    for (j = 0;ch = smtpcmd[i].text[j];++j)
  628.      if ((cmd[j] != ch) && (cmd[j] != ch - 32))
  629.        break;
  630.    if (!ch)
  631.      if (!cmd[j] || (cmd[j] == ' '))
  632.       {
  633.        while (cmd[j] == ' ') ++j;
  634.        if (!cmd[j])
  635.          smtpcmd[i].fun((char *) 0);
  636.        else
  637.          smtpcmd[i].fun(cmd + j);
  638.        if (smtpcmd[i].flagflush) flush();
  639.        return;
  640.       }
  641.   }
  642.  err_unimpl();
  643.  flush();
  644. }
  645.  
  646. void getcontrols()
  647. {
  648.  if (control_init() == -1) die();
  649.  if (control_rldef(&greeting,"control/smtpgreeting",1,(char *) 0) != 1) die();
  650.  switch(control_rldef(&liphost,"control/localiphost",1,(char *) 0))
  651.   { case -1: die(); case 1: liphostok = 1; }
  652.  if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die();
  653.  if (timeout <= 0) timeout = 1;
  654.  switch(control_readfile(&rcpthosts,"control/rcpthosts",0))
  655.   {
  656.    case -1: die();
  657.    case 1:
  658.      rhok = 1;
  659.      if (!constmap_init(&maprcpthosts,rcpthosts.s,rcpthosts.len,0)) die();
  660.   }
  661.  switch(control_readfile(&bmf,"control/badmailfrom",0))
  662.   {
  663.    case -1: die();
  664.    case 1:
  665.      bmfok = 1;
  666.      if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die();
  667.   }
  668. #ifdef BADRCPT
  669.  switch(control_readfile(&brt,"control/badrcptto",0))
  670.   {
  671.    case -1: die();
  672.    case 1:
  673.      brtok = 1;
  674.      if (!constmap_init(&mapbadrcptto,brt.s,brt.len,0)) die();
  675.   }
  676. #endif
  677. }
  678.  
  679. void main()
  680. {
  681.  static stralloc cmd = {0};
  682.  int match;
  683.  
  684.  sig_alarmcatch(sigalrm);
  685.  sig_pipeignore();
  686.  
  687. #ifdef SYSLOGGING
  688.   openlog ("qmail-smtpd", 0, LOG_MAIL);
  689. #endif
  690.  
  691.  if (chdir(auto_qmail) == -1)
  692.  {
  693. #ifdef SYSLOGGING
  694.    syslog (LOG_ERR, "Unable to switch to home directory (%s): errno=%m",
  695.            auto_qmail);
  696. #endif
  697.    die();
  698.  }
  699.  getcontrols();
  700.  getenvs();
  701.  
  702.  if (ipme_init() != 1) die();
  703.  
  704.  smtp_greet("220 ");
  705.  out(" ESMTP\r\n");
  706.  
  707.  for (;;)
  708.   {
  709.    /* XXX: recipient can contain quoted lf. aargh. */
  710.    if (getln(&ssin,&cmd,&match,'\n') == -1) die();
  711.    if (!match) die();
  712.    if (cmd.len == 0) die();
  713.    if (cmd.s[--cmd.len] != '\n') die();
  714.    if ((cmd.len > 0) && (cmd.s[cmd.len - 1] == '\r')) --cmd.len;
  715.    cmd.s[cmd.len++] = 0;
  716.    doit(cmd.s);
  717.   }
  718. #ifdef SYSLOGGING
  719.   closelog ();
  720. #endif
  721. }
  722.